//TODO: check why this is producing no result as all (only black pixels at the moment)

using System;
using System.Drawing;
using System.Drawing.Imaging;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// ImageBitShift is an algorithm to apply arithmetic BitShift on a image
    /// </summary>
    public class ImageBitShift : BaseImageFilter
    {
        protected bool m_bIsColored;
        protected int m_nBitShiftLeft = 1;
        protected bool m_bWrap = false;
        protected int m_nConstant = 0;

        /// <summary>
        /// Constructs a new Image Operator
        /// </summary>
        public ImageBitShift()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageBitShift"/> class.
        /// </summary>
        /// <param name="_nBitShiftLeft">The _n bit shift left.</param>
        public ImageBitShift(int _nBitShiftLeft)
            : this(0, _nBitShiftLeft, false)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageBitShift"/> class.
        /// </summary>
        /// <param name="_nConstant">The _n constant.</param>
        /// <param name="_nBitShiftLeft">The _n bit shift left.</param>
        /// <param name="_bWrap">if set to <c>true</c> [_b wrap].</param>
        public ImageBitShift(int _nConstant, int _nBitShiftLeft, bool _bWrap)
            : this()
        {
            m_nConstant = _nConstant;
            m_nBitShiftLeft = _nBitShiftLeft;
            m_bWrap = _bWrap;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ImageBitShift"/> class.
        /// </summary>
        /// <param name="_imageBitShift">The _image bit shift.</param>
        internal ImageBitShift(ImageBitShift _imageBitShift)
        {
            this.m_nConstant = _imageBitShift.m_nConstant;
            this.m_nBitShiftLeft = _imageBitShift.m_nBitShiftLeft;
            this.m_bWrap = _imageBitShift.m_bWrap;
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is colored.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is colored; otherwise, <c>false</c>.
        /// </value>
        public bool IsColored
        {
            get { return m_bIsColored; }
            set { m_bIsColored = value; }
        }

        /// <summary>
        /// Gets or sets the bit shift left.
        /// </summary>
        /// <value>The bit shift left.</value>
        public int BitShiftLeft
        {
            get { return m_nBitShiftLeft; }
            set { m_nBitShiftLeft = value; }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="ImageBitShift"/> is wrap.
        /// </summary>
        /// <value><c>true</c> if wrap; otherwise, <c>false</c>.</value>
        public bool Wrap
        {
            get { return m_bWrap; }
            set { m_bWrap = value; }
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            DoBitShift(_bitmap, 0, true, true, 0, 1);
            return _bitmap;
        }

        /// <summary>
        /// BitShifts the specified input image by the constant, also adds offset
        /// and scales
        /// </summary>
        /// <param name="_bitmap">src1_1d The input pixel array</param>
        /// <param name="nConstant">The constant value to shift every pixel value by</param>
        /// <param name="bShiftLeft">Boolean to specify a left or right bit shift</param>
        /// <param name="bWrap">wrap Boolean to specify if the shifted values should be wrapped</param>
        /// <param name="fOffset">The offset value</param>
        /// <param name="fScale">The scale value</param>
        private void DoBitShift(UnsafeBitmap _bitmap, int nConstant, bool bShiftLeft,
                     bool bWrap, float fOffset,
                     float fScale)
        {
            //Get size of image and make 1d_arrays

            int src1rgb;
            int nResult = 0;

            int[] wrapArray = new int[9];

            nConstant = nConstant % 8;

            wrapArray[0] = 0;
            wrapArray[1] = 128;
            wrapArray[2] = 192;
            wrapArray[3] = 224;
            wrapArray[4] = 240;
            wrapArray[5] = 248;
            wrapArray[6] = 252;
            wrapArray[7] = 254;
            wrapArray[8] = 255;

            UnsafeBitmap bitmap = _bitmap;

            uint nColorResult = 0;

            int nHeight = bitmap.Height;
            int nWidth = bitmap.Width;

            //now do the BitShift on the input image 
            for (int y = 0; y < nHeight; y++)
            {
                for (int x = 0; x < nWidth; x++)
                {
                    Color bitmapGetPixel = bitmap.GetPixel(x, y);
                    int nValue = ColorFunctions.ToIntRGB(bitmapGetPixel);
                    //This works for unwrapped shifting
                    if (bWrap == false)
                    {
                        src1rgb = nValue & 0x000000ff;
                        if (bShiftLeft)
                        {
                            nResult = (src1rgb << nConstant);
                        }
                        else
                        {
                            nResult = (src1rgb >> nConstant);
                        }

                        // mask to get result bits
                        nResult = nResult & 0x000000ff;

                        // rescale for display
                        nResult = (int)(fScale * (float)nResult + fOffset);
                        if (nResult > 255) nResult = 255;
                        if (nResult < 0) nResult = 0;

                        nColorResult = (uint)(nResult + (nResult << 16) + (nResult << 8)); //Original code: (0xFF << 24 | (nResult + (nResult << 16) + (nResult << 8)));
                        bitmap.SetPixel(x, y, Color.FromArgb((int)nColorResult));

                    }
                    else if (bWrap == true)
                    {
                        int nTemp;
                        src1rgb = nValue & 0x000000ff;
                        if (bShiftLeft)
                        {
                            nResult = 0;
                            nTemp = wrapArray[nConstant] & src1rgb;
                            nTemp = nTemp >> (8 - nConstant);
                            nResult = (src1rgb << nConstant);
                            nResult = nResult | nTemp;
                        }
                        else
                        {
                            //Shifting left 3 = shift right by 8-3, so just subtract shift
                            //value from 8.
                            nResult = 0;
                            nConstant = 8 - nConstant;
                            nTemp = wrapArray[nConstant] & src1rgb;
                            nTemp = nTemp >> (8 - nConstant);
                            nResult = (src1rgb << nConstant);
                            nResult = nResult | nTemp;
                        }

                        // mask to get result bits
                        nResult = nResult & 0x000000ff;

                        // rescale for display
                        nResult = (int)(fScale * (float)nResult + fOffset);
                        if (nResult > 255) nResult = 255;
                        if (nResult < 0) nResult = 0;
                        nColorResult = ((uint)(0xFF << 24 | (nResult + (nResult << 16) + (nResult << 8))));
 
                        bitmap.SetPixel(x, y, Color.FromArgb((int)nColorResult));
                    }
                }
            }
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new ImageBitShift(this);
        }
    }
}